
(function(window,document){
    
function defineSingleonProperty(target:any,name:string|{[name:string]:(self)=>any},getter?:(self)=>any){
    if(typeof name ==="object"){
        for(const n in name){
            defineSingleonProperty(target, n,name[n]);
        }
        return;
    }
    let privateName = "$YA__" + name + "__";
    Object.defineProperty(target,name,{configurable:false,get:function(){
        if(this[privateName]===undefined){
            Object.defineProperty(target,privateName,{configurable:false,enumerable:false,writable:false, value:getter(this)});
        }
        return this[privateName];
    }});
};
    //=======================================================================
    // Promise /异步
    type TAsyncStatement=(resolve:(result:any)=>any,reject:(err:any)=>any)=>any;
    interface IThenable{
        then(fulfillCallback:(result)=>any,rejectCallback?:(result)=>any):IThenable;
    }
    enum PromiseStates{
        Pending=0,
        Fulfilled=1,
        Rejected=-1
    }
    class Promise implements IThenable{
        $_promise_status:PromiseStates;
        $_promise_fulfillCallbacks:{(result:any,isSuccess?:boolean):any}[];
        $_promise_rejectCallbacks:{(result:any,isSuccess?:boolean):any}[];
        $_promise_result:any;
        
        constructor(statement?:TAsyncStatement,sync?:boolean){
            let status =this.$_promise_status= PromiseStates.Pending;
            let result =this.$_promise_result = undefined;
            let fulfillCallbacks:{(result:any,isSuccess?:boolean):any}[] = this.$_promise_fulfillCallbacks=[];
            let rejectCallbacks:{(result:any,isSuccess?:boolean):any}[] = this.$_promise_rejectCallbacks =[];
        
        
            let resolve =(result:any):Promise=>{
                if(status!==PromiseStates.Pending){ 
                    console.warn("settled状态不应该再调用resolve/reject");
                    return this; 
                }
                
                //如果是自己，就丢出错误
                if(result===this) throw new TypeError("不能把自己resolve掉啊.");
                //resolve的结果是了一个thenable
                if(result && typeof result.then ==="function"){
                    //让该Promise的状态跟resolve result的状态保持一致
                    result.then(
                        (value)=>fulfill(value)
                        ,(value)=>reject(value)
                    );
                }else {
                    //如果是其他的类型，就让promise 变更为fulfill状态
                    fulfill(result);
                }
                
                return this;
            };
            let reject = (value:any):Promise=>{
                if(status!==PromiseStates.Pending){ 
                    console.warn("settled状态不应该再调用resolve/reject");
                    return this; 
                }
                
                status = this.$_promise_status = PromiseStates.Fulfilled;
                result = this.$_promise_result = value;
                this.resolve = this.reject=function (params:any):Promise { return this; }

                setTimeout(()=>{
                    let rejectHandlers = rejectCallbacks;
                    
                    this.$_promise_fulfillCallbacks = this.$_promise_rejectCallbacks
                    =fulfillCallbacks = rejectCallbacks =null;

                    for(const i in rejectHandlers)
                        rejectHandlers[i].call(this,result,false);                    
                },0);
                return this;
            };
            let fulfill = (value:any)=>{
                if(status!==PromiseStates.Pending) {
                    //循环引用，给个警告，什么都不做
                    console.warn("已经处于Settled状态，无法再更正状态");
                    return;
                }

                status = this.$_promise_status = PromiseStates.Fulfilled;
                result = this.$_promise_result = value;
                let complete = ()=>{
                    let fulfillHandlers = fulfillCallbacks;
                    this.$_promise_fulfillCallbacks = this.$_promise_rejectCallbacks
                    =fulfillCallbacks = rejectCallbacks = null;

                    for(const i in fulfillHandlers)
                        fulfillHandlers[i].call(this,result,true);
                    
                };
                setTimeout(complete,0);

            };
            // ajax().then((rs)=>ajax1()).then
            this.then = (fulfillHandler:(result)=>any,rejectHandler?:(result)=>any):Promise=>{
                if(status ===PromiseStates.Fulfilled && fulfillHandler){
                    setTimeout(()=>{
                        fulfillHandler.call(this,result,true);
                    },0);
                }
                if(status===PromiseStates.Rejected && rejectHandler){
                    setTimeout(()=>{
                        rejectHandler.call(this,result,false);
                    },0);
                }
                if(status !==PromiseStates.Pending) return this;
                
                if(!fulfillHandler && !rejectHandler) return this;
                
                let innerResolve;
                let innerReject;
                let newPromise = new Promise((resolve,reject)=>{
                    innerResolve = resolve;
                    innerResolve =reject;
                });
                
                if(fulfillHandler){
                    fulfillCallbacks.push((value:any)=>{
                        let rs = fulfillHandler.call(this,value,true);
                        if(rs && typeof rs.then ==="function"){ rs.then(innerResolve,innerReject); }
                        else innerResolve.call(newPromise,rs);
                    });
                    
                }
                if(rejectHandler){
                    rejectCallbacks.push((value:any)=>{
                        rejectHandler.call(this,value,false);
                        innerResolve(undefined);
                    });
                }
                return newPromise;
                
            }

            if(statement){
                if(sync) {
                    try{
                        statement.call(this,resolve,reject);
                    }catch(ex){
                        reject(ex);
                    }
                }else setTimeout(() => {
                    try{
                        statement.call(this,resolve,reject);
                    }catch(ex){
                        reject(ex);
                    }
                }, 0);
            }else{
                this.resolve = (value:any)=>{setTimeout(()=>resolve(value),0);return this};
                this.reject = (value:any)=>{setTimeout(()=>reject(value),0);return this};
            }
            
        }
        then(fulfillCallback:(result)=>any,rejectCallback?:(result)=>any):Promise{
            console.warn("called on placehold method.");
            return this;
        }
        
        resolve(result:any):Promise{
            console.warn("当Promise设置了异步函数时，resolve/reject应该由Promise的异步函数调用");
            return this;
        }
        reject(result:any):Promise{
            console.warn("当Promise设置了异步函数时，resolve/reject应该由Promise的异步函数调用");
            return this;
        }
        success(callback:(result)=>any):Promise{
            return this.then(callback);
        }
        error(callback:(result)=>any):Promise{
            return this.then(undefined,callback);
        }
        complete(callback:(result)=>any):Promise{
            return this.then(callback,callback);
        }
        catch(callback:(result)=>any):Promise{
            return this.then(undefined,callback);
        }
        static resolve(value:any):Promise{
            return new Promise((resolve,reject)=>resolve(value));
        }
        static reject(value:any):Promise{
            return new Promise((resolve,reject)=>reject(value));
        }
        static all(thenables:IThenable[],sync?:boolean):IThenable{
            return new Promise((resolve,reject)=>{
                let waitCount = thenables.length;
                let rs = [];
                for(const i in thenables)((thenable:IThenable,i:number)=>{
                    thenables[i].then((value)=>{
                        if(rs){
                            rs[i]=value;
                            if(--waitCount==0) resolve(rs);
                        }
                    },(err)=>{
                        rs = undefined;
                        reject(err);
                    });
                })(thenables[i],i as any);
            },sync);
        }
    }
    if(typeof window!=='undefined') {
        if(!(window as any).Promise) (window as any).Promise= Promise;
    }

    interface IHttpRequestOpts{
        method?:"GET"|"POST"|"PUT"|"DELETE"|"OPTIONS"|"HEADER";
        url?:string;
        headers?:{[name:string]:string|[]};
        async?:boolean;
        nocache?:boolean;
        data?:any;
        type?:""|"json"|"url-encoding"|"blob";
        contentType?:string;
        responseType?:"" | "arraybuffer" | "blob" | "document" | "json" | "text" |"response";
    }

        
    class HttpRequest extends Promise{
        xhr:XMLHttpRequest;
        method:"GET"|"POST"|"PUT"|"DELETE"|"OPTIONS"|"HEADER";
        url:string;
        headers:{[name:string]:string|[]};
        async:boolean;
        data:any;
        type:""|"json"|"url-encoding"|"blob";
        responseType?:"" | "arraybuffer" | "blob" | "document" | "json" | "text" | "xml"|"response";

        constructor(public opts:IHttpRequestOpts){
            super((resolve,reject)=>{
                let xhr = this.xhr;//new XMLHttpRequest
                let method = this.method;
                xhr.open(method,this.url,this.opts.async!==false);
                let responseType = this.responseType;
                xhr.responseType = responseType==="xml"?"":(responseType==="response"?"":responseType);
                let reqHeaders = this.headers;
                xhr.onreadystatechange = ()=> {
                //准备状态改变触发
                    if(xhr.readyState == 4) {
                        if(xhr.status !== 200) {
                            reject("http error:"+xhr.status);
                            return ;
                        }else{
                            let result;
                            try{
                                result = new HttpResponse(xhr,this);
                            }catch(ex) {
                                return reject(ex);
                            }
                            resolve(result.data);
                        }

                    }
                }
                xhr.onerror = reject;
                if(method==="POST" || method==="PUT"){
                    let contentType = this.opts.contentType;
                    if(!contentType) reqHeaders?(reqHeaders["contentType"]||reqHeaders["Content-Type"]):null;
                    if(!contentType) contentType = "application/x-www-form-urlencoded";
                    xhr.setRequestHeader("Content-Type",contentType);

                }

                //xmlrequest.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

                
                xhr.send(this.data);
            });
            
        }
        static optsToken:string = "AJAX_OPTS";
    }
    defineSingleonProperty(HttpRequest.prototype,{
        "method":(self:HttpRequest)=>self.opts.method?self.opts.method.toUpperCase():"GET"
        ,"type":(self:HttpRequest)=>self.opts.type|| "url-encoding"
        ,"url":(self:HttpRequest)=>{
            let url = self.opts.url;
            if(url===undefined) url = window.location.href;
            let method = self.method;
            if(method==="GET" || method==="DELETE" || method==="OPTIONS"){
                if(url.indexOf("?")<0){ url += "?"; url += this.data;}
                else {
                    url+="&" + this.data;
                }
                if(self.opts.nocache!==false) url +="&__="+Math.random();
            }
            return url;
        }
        ,"data":(self:HttpRequest)=>{
            let data = self.opts.data;
            let type = self.type;
            if(type==="blob")return data===undefined?null:data;
            let t = typeof data;
            if(type==="json"){
                if(t==="string") return data;
                else return JSON.stringify(data);
            }else {
                let str = "";
                if(t==="object"){
                    for(const n in data){
                        if(str) str+= "&";
                        str += encodeURIComponent(n);
                        str +="=";
                        str += encodeURIComponent(data[n]);
                    }
                    return str;
                }
                return data===null||data===undefined?"":data.toString();
            }
        }
        ,"xhr":(self)=>{
            var xhr = null;
            if(window.XMLHttpRequest){//判断当前浏览器是否支持XMLHttpRequest
                xhr = new XMLHttpRequest();
            }else if(window.XMLHttpRequest){//判断当前浏览器是否支持XMLHttpRequest，这是对于IE浏览器的判断
                try{
                    xhr = new ActiveXObject("Msxml2.XMLHTTP");//IE6及以后的版本支持的
                }catch(e){
                    try{
                        xhr = new ActiveXObject('Microsoft.XMLHTTP');//IE6以下版本的支持
                    }catch(e){}
                }
            }
            return xhr;
        }
        ,"headers":(self:HttpRequest)=>{
            let optsHeaders = self.opts.headers;
            let headers = {};
            for(let n in optsHeaders){
                let value = optsHeaders[n] as string;
                headers[n]=value;
                self.xhr.setRequestHeader(n,value);
            }
            return headers;
        }
        ,"responseType":(self:HttpRequest)=>self.opts.responseType||"text"
        
    });
    class HttpResponse{
        data:any;
        status:number;
        constructor(public xhr:XMLHttpRequest,public request:HttpRequest){
            this.status = xhr.status;
            if(this.status!==200){
                
            }
            let responseType = request.responseType;
            if(responseType==="json"){
                this.data = xhr.response;
            }else if(responseType==="blob"||responseType=="arraybuffer"){
                this.data = xhr.response;
            }else if(responseType==="xml"){
                this.data = xhr.responseXML;
            }else if(responseType==="response"){
                this.data = this;
            } else if(responseType) this.data = xhr.responseText;
        }
        header(name:string):string|null{
            return this.xhr.getResponseHeader(name);
        }
    }
    (window as any).ajax = function(opts){
        let ajaxOpts = {
            url:opts.url,
            method:opts.type,
            responseType:opts.dataType,
            data:opts.data
        };
        return new HttpRequest(ajaxOpts);
    }

    const rootElement = document.compatMode=="CSS1Compat"?document.documentElement:document.body;
    const appHeader = document.getElementById("app-header");
    const waitingElement:HTMLElement = document.getElementById("waiting");
    const appElement:HTMLElement = document.getElementById("app");
    const loginElement = document.getElementById("login-form");
    const loginBtn = document.getElementById("login-btn");
    
    const workarea = document.getElementById("workarea");
    const nav = document.getElementById("nav");
    const chats = document.getElementById("chats");
    const chatlist = document.getElementById("chat-list");
    const search = document.getElementById("search");

    

    let chatApp:any=(window as any).chatApp={};
    let isWaiting=false;
    chatApp.waiting=function(txt){
        let centerWaiting = function(){
            waitingElement.style.top = (rootElement.clientHeight - waitingElement.clientHeight)/2 +"px";
            waitingElement.style.left = (rootElement.clientWidth - waitingElement.clientWidth)/2 + "px";
        };
        if(txt!==false){
            waitingElement.style.display="block";
            chatApp.showMain(false);
            chatApp.login(false);
            loginElement.style.display = "none";    
            if(txt===true || txt===undefined || txt===null) txt="";
            waitingElement.lastElementChild.innerHTML = txt;
            centerWaiting();
            if(!isWaiting)window.addEventListener("resize",centerWaiting);
            isWaiting = true;
        }else {
            waitingElement.style.display="none";
            if(isWaiting) window.removeEventListener("resize",centerWaiting);
            isWaiting = false;
        }
        return this;
    }

    let isLoginShow =false;
    chatApp.login=function(onSubmit){
        let centerLogin = function(){
            loginElement.style.top = (rootElement.clientHeight - loginElement.clientHeight)/2 + "px";
            loginElement.style.left = (rootElement.clientWidth - loginElement.clientWidth)/2 + "px";
        };

        if(onSubmit===false){
            loginElement.style.display="none";
            if(isLoginShow) window.removeEventListener("resize",centerLogin);
            isLoginShow = false;
        }else{
            loginElement.style.display="block";
            chatApp.showMain(false);chatApp.waiting(false);
            
            centerLogin();
            if(!isLoginShow) window.addEventListener("resize",centerLogin);
            isLoginShow = true;
            if(typeof onSubmit==="function") loginBtn.onclick = onSubmit;
        }
    }
    let isMainShow;
    chatApp.showMain = function(onSend){
        let adjustSizes = function(){
            const currChat = document.getElementById("current-chat");
            const currChatCaption = document.getElementById("current-chat-caption");
            const messages = document.getElementById("messages");
            const opElement = document.getElementById("op");
            let h = rootElement.clientHeight-2;
            chats.style.height = currChat.style.height = nav.style.height = h + "px";
            chatlist.style.height = h-search.clientHeight- appHeader.clientHeight + "px";
            messages.style.height = h- currChatCaption.clientHeight - opElement.clientHeight - appHeader.clientHeight + "px";
        };
        if(onSend!==false){
            appElement.style.display = "block";
            chatApp.waiting(false);chatApp.login(false);
            if(!isMainShow) window.addEventListener("resize",adjustSizes);
            adjustSizes();
            if(typeof onSend==="function"){
                document.getElementById("send-btn").onclick = onSend;
            }
            isMainShow = true;
        }else{
            appElement.style.display ="none";
            if(isMainShow) window.removeEventListener("resize",adjustSizes);
            isMainShow = false;
        }
    }
    chatApp.attechWindowEvents=function(opts){

    }

    


    chatApp.waiting("启动中...");
})(window,document);


